package model.semantics;

import java.util.*;

import model.State;
import model.VariableTable;
import model.parser.UserFunctionDefinition;

public class UserFunctionNode<T> extends Node<T> {
    private UserFunctionDefinition<T> myDefinition;
    private Map<String, Node<T>> myArgMap;

    public UserFunctionNode(UserFunctionDefinition<T> definition,
            Map<String, Node<T>> argMap) {
        myDefinition = definition;
        myArgMap = argMap;
    }

    /**
     * Evaluation works by creating a new state with the function arguments
     * filled in (the local scope, if you will), then calling.
     */
    @Override
    public T evaluate(State<T> state) {
        VariableTable<T> outerTable = state.getVariableTable();

        // Create inner variable table for function.
        VariableTable<T> innerTable = outerTable.clone();

        for (String argument : myArgMap.keySet()) {
            // Evaluate function arguments and set them in the inner table to
            // pass them in.
            T value = myArgMap.get(argument).evaluate(state);
            innerTable.setAssignment(argument, new ConstNode<T>(value));
        }

        state.setVariableTable(innerTable);

        T out = myDefinition.getBody().evaluate(state);

        state.setVariableTable(outerTable);

        return out;
    }

    @Override
    public String toString() {
        return String.format("(fun %s)", myArgMap.values().toString());
    }

}
